import { httpError, Inject, Provide } from '@midwayjs/core'
import { Context } from '@midwayjs/koa'
import { InjectEntityModel } from '@midwayjs/typeorm'
import { Between, In, Repository } from 'typeorm'

import { DeleteDTO } from '@/dto'
import { SysMenuEntity, SysRoleEntity, SysUserEntity } from '@/entity'
import { StateService } from '@/service'

import {
  CreateRoleDTO,
  FindAuthUserPageDTO,
  FindRolePageDTO,
  UpdateRoleDTO
} from './role.dto'

@Provide()
export class RoleService {
  @Inject()
  ctx: Context

  @InjectEntityModel(SysMenuEntity)
  menuModel: Repository<SysMenuEntity>

  @InjectEntityModel(SysRoleEntity)
  roleModel: Repository<SysRoleEntity>

  @InjectEntityModel(SysUserEntity)
  userModel: Repository<SysUserEntity>

  async handleFindPage(dto: FindRolePageDTO) {
    const { pageNum, pageSize, beginDate, endDate, ...where } = dto

    const [list, total] = await this.roleModel.findAndCount({
      relations: {
        menus: true
      },
      select: {
        menus: {
          menuId: true
        }
      },
      where: {
        delFlag: '0',
        createTime: beginDate ? Between(beginDate, endDate) : null,
        ...where
      },
      order: {
        roleSort: 'ASC'
      },
      take: pageSize,
      skip: (pageNum - 1) * pageSize
    })

    return {
      pageNum,
      pageSize,
      total,
      list
    }
  }

  async handleFindAuthUserPage(id: string, dto: FindAuthUserPageDTO) {
    const { pageNum, pageSize, ...where } = dto

    const [list, total] = await this.userModel.findAndCount({
      relations: { roles: true },
      where: {
        roles: { roleId: id },
        ...where
      },
      skip: (pageNum - 1) * pageSize,
      take: pageSize
    })

    return {
      pageNum,
      pageSize,
      total,
      list
    }
  }

  async handleCancelAuthUser(id: string, dto: DeleteDTO) {
    const { ids: userIds } = dto
    const role = await this.roleModel.findOne({
      relations: { users: true },
      where: { roleId: id }
    })

    role.users = role.users.filter(v => !userIds.includes(v.userId))

    await this.roleModel.save(role)
  }

  async handleCreate(dto: CreateRoleDTO) {
    const stateService = await this.ctx.requestContext.getAsync(StateService)

    const jwtPayload = stateService.getJwtPayload()
    const { menus, ...rest } = dto

    const role = new SysRoleEntity()

    Object.assign<any, any, Partial<SysRoleEntity>>(role, rest, {
      createBy: jwtPayload.userName
    })

    if (menus.length > 0) {
      role.menus = await this.menuModel.find({
        select: { menuId: true },
        where: menus.map(v => ({ menuId: v }))
      })
    }

    await this.roleModel.save(role)
  }

  async handleUpdate(id: string, dto: UpdateRoleDTO) {
    const stateService = await this.ctx.requestContext.getAsync(StateService)

    const jwtPayload = stateService.getJwtPayload()

    const role = await this.roleModel.findOne({
      relations: { menus: true },
      where: { roleId: id }
    })

    if (!role) {
      this.ctx.throw(new httpError.BadRequestError('角色不存在'))
    }

    const { menus, ...rest } = dto

    Object.assign<any, any, Partial<SysRoleEntity>>(role, rest, {
      updateBy: jwtPayload.userName
    })

    if (menus.length > 0) {
      role.menus = await this.menuModel.find({
        select: { menuId: true },
        where: menus.map(v => ({ menuId: v }))
      })
    }

    await this.roleModel.save(role)
  }

  async handleDelete(dto: DeleteDTO) {
    const { ids } = dto

    const list = await this.roleModel.find({
      relations: { menus: true },
      where: { roleId: In(ids) }
    })

    for (const role of list) {
      role.delFlag = '2'
      role.menus = []

      await this.roleModel.save(role)
    }
  }

  async handleToggleStatus(id: string) {
    const role = await this.roleModel.findOne({ where: { roleId: id } })

    if (!role) {
      this.ctx.throw(new httpError.BadRequestError('角色不存在'))
    }

    role.status = role.status === '0' ? '1' : '0'

    await this.roleModel.save(role)
  }
}
